#include "fxcc/core/graph/opengl3/ssao/LightingEffect.h"

using namespace fxcc::graph::opengl3::ssao;

LightingEffect::LightingEffect()
    : fxcc::graph::opengl3::Effect3d(LightingEffect::FragCode())
{
    m_LightBuffer = std::make_shared<fxcc::graph::opengl3::UniformBuffer>(sizeof(Light));
}

void LightingEffect::Begin() {

    m_LightBuffer->Binding(3);

    m_Shader->Use();
    m_Shader->Bind("LightBuffer", 3);
    m_Shader->setInt("gPosition", 0);
    m_Shader->setInt("gNormal", 1);
    m_Shader->setInt("gAlbedo", 2);
    m_Shader->setInt("ssao", 3);

    fxcc::graph::opengl3::Effect3d::Begin();
};

void LightingEffect::SetViewFragPosMap(const fxcc::graph::opengl3::Texture2DJack *texture) {
    texture->Active(0);
};

void LightingEffect::SetViewNormalMap(const fxcc::graph::opengl3::Texture2DJack *texture){
    texture->Active(1);
};
void LightingEffect::SetAlbedoMap(const fxcc::graph::opengl3::Texture2DJack *texture)
{
    texture->Active(2);
};

void LightingEffect::SetSSAOMap(const fxcc::graph::opengl3::Texture2DJack *texture) {
    texture->Active(3);
};

void LightingEffect::SetLight(const Light &light)
{
    m_LightBuffer->Upload<Light>(light);
}

std::string LightingEffect::FragCode() 
{
    return R"(
out vec4 FragColor;
    
uniform sampler2D gPosition;
uniform sampler2D gNormal;
uniform sampler2D gAlbedo;
uniform sampler2D ssao;

struct FXCC_API  Light {
    vec3 Position;
    float Linear;
    vec3 Color;
    
    float Quadratic;
};

layout(std140) uniform LightBuffer
{
    Light light;
};

void main()
{             
    vec2 TexCoord=vOut.TexCoords;
    // retrieve data from gbuffer
    vec3 FragPos = texture(gPosition, TexCoord).rgb;
    vec3 Normal = texture(gNormal, TexCoord).rgb;
    vec3 Diffuse = texture(gAlbedo, TexCoord).rgb;
    float AmbientOcclusion = texture(ssao, TexCoord).r;
    
    // then calculate lighting as usual
    vec3 ambient = vec3(0.3 * Diffuse * AmbientOcclusion);
    vec3 lighting  = ambient; 
    vec3 viewDir  = normalize(-FragPos); // viewpos is (0.0.0)
    // diffuse
    vec3 lightDir = normalize(light.Position - FragPos);
    vec3 diffuse = max(dot(Normal, lightDir), 0.0) * Diffuse * light.Color;
    // specular
    vec3 halfwayDir = normalize(lightDir + viewDir);  
    float spec = pow(max(dot(Normal, halfwayDir), 0.0), 8.0);
    vec3 specular = light.Color * spec;
    // attenuation
    float distance = length(light.Position - FragPos);
    float attenuation = 1.0 / (1.0 + light.Linear * distance + light.Quadratic * distance * distance);
    diffuse *= attenuation;
    specular *= attenuation;
    lighting += diffuse + specular;

    FragColor = vec4(lighting, 1.0);

}

    )";
};

